/*
 * Copyright (c) 2004-2005 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Kevin Lim
 */

#include "cpu/edge/pred/ras.hh"
#include "cpu/base.hh"

template<class Impl>
void
ReturnAddrStack<Impl>::init(unsigned _numEntries, unsigned _lsEntries,unsigned _blockShiftAmt)
{
     numEntries  = _numEntries;
     lsEntries = _lsEntries;
     usedEntries = 0;
     tos = 0;

     addrStack.resize(numEntries);

     for (unsigned i = 0; i < numEntries; ++i)
         addrStack[i] = 0;

     rasStackMask = numEntries - 1;
 
     rasStackPointer.resize(Impl::MaxThreads);

     for (unsigned i = 0; i < Impl::MaxThreads; ++i)
	  rasStackPointer[i] = 0;

     rasHistoryFile.resize(Impl::MaxInFlightBlockNum);
    
     for (unsigned i = 0; i < Impl::MaxInFlightBlockNum; ++i) {
         rasHistoryFile[i].tos = 0;
	  rasHistoryFile[i].target = 0;
     }
	 
     rasLSPointer.resize(Impl::MaxThreads);

     for (unsigned i = 0; i < Impl::MaxThreads; ++i)
	  rasLSPointer[i] = 0;

     rasLSMask = lsEntries - 1;

     rasLinkStack.resize(lsEntries);

     for (unsigned i = 0; i < lsEntries ; ++i) {
         rasLinkStack[i].index = 0;
	  rasLinkStack[i].block_addr = 0;
     }

     blockShiftAmt = _blockShiftAmt;
 
     DPRINTF(EdgeRAS,"RAS: blockShiftAmt = %i.\n",blockShiftAmt);
}

template<class Impl>
void
ReturnAddrStack<Impl>::reset()
{
    usedEntries = 0;
    tos = 0;
    for (unsigned i = 0; i < numEntries; ++i)
        addrStack[i] = 0;
	
    for (unsigned i = 0; i < Impl::MaxThreads; ++i)
	 rasStackPointer[i] = 0;

    
    for (unsigned i = 0; i < Impl::MaxInFlightBlockNum; ++i) {
        rasHistoryFile[i].tos = 0;
	 rasHistoryFile[i].target = 0;
    }
	 
    for (unsigned i = 0; i < Impl::MaxThreads; ++i)
	 rasLSPointer[i] = 0;

    for (unsigned i = 0; i < lsEntries ; ++i) {
        rasLinkStack[i].index = 0;
	 rasLinkStack[i].block_addr = 0;
    }
}

template<class Impl>
void
ReturnAddrStack<Impl>::push(const Addr return_addr, ThreadID tid)
{
    unsigned tos = rasStackPointer[tid];
    unsigned tos_new = (tos + 1) & rasStackMask;
    rasStackPointer[tid] = tos_new;
    addrStack[tos_new] = return_addr;
    if (usedEntries != numEntries) {
        ++usedEntries;
    }
    DPRINTF(EdgeRAS,"RAS push:TOS = %i. Addr = %#x.\n",tos_new,return_addr<<7);
}

template<class Impl>
void
ReturnAddrStack<Impl>::pop(ThreadID tid)
{
    unsigned tos = rasStackPointer[tid];
    unsigned tos_new = (tos - 1) & rasStackMask;
    rasStackPointer[tid] = tos_new;
    if (usedEntries > 0) {
        --usedEntries;
    }
    DPRINTF(EdgeRAS,"RAS pop:TOS = %i.\n",tos_new);
}

template<class Impl>
Addr
ReturnAddrStack<Impl>::targetGen(Addr target)
{
    return target << blockShiftAmt;
}

template<class Impl>
Addr
ReturnAddrStack<Impl>::lookup(BlockID blockID,ThreadID tid)
{
    unsigned tos = rasStackPointer[tid];
    Addr target = addrStack[tos];
    RasHFEntry rasHFEntry;
    rasHFEntry.tos = tos;
    rasHFEntry.target = target;
    rasHistoryFile[blockID&(Impl::MaxInFlightBlockNum -1)] = rasHFEntry;
    return target;
}

template<class Impl>
void
ReturnAddrStack<Impl>::squash(BlockID blockID,ThreadID tid)
{
    unsigned index = blockID&(Impl::MaxInFlightBlockNum -1);
    unsigned tos;
    RasHFEntry rasHFEntry;
    rasHFEntry = rasHistoryFile[index];
    tos = rasHFEntry.tos;
    rasStackPointer[tid] = tos;
    addrStack[tos] = rasHFEntry.target;
    DPRINTF(EdgeRAS,"Squashing RAS to block [bid:%i]."
		"TOS = %i and target is %#x.\n",blockID,tos,addrStack[tos]);
}

template<class Impl>
void
ReturnAddrStack<Impl>::lspush(Addr block_addr,unsigned index, ThreadID tid)
{
    unsigned tos = rasLSPointer[tid];
    unsigned tos_new = (tos + 1) & rasLSMask;
    LSEntry lsEntry;
    rasLSPointer[tid] = tos_new;
    lsEntry.index = index;
    lsEntry.block_addr = block_addr;
    rasLinkStack[tos_new] = lsEntry;
    DPRINTF(EdgeRAS,"Store index(%i) and PC(%#x) to LS. LS TOS is %i\n",
		index,block_addr,tos_new);
}

template<class Impl>
unsigned
ReturnAddrStack<Impl>::lsPopIdx(ThreadID tid)
{
    unsigned tos = rasLSPointer[tid];
    return rasLinkStack[tos].index;
}

template<class Impl>
Addr
ReturnAddrStack<Impl>::lsPopAddr(ThreadID tid)
{
    unsigned tos = rasLSPointer[tid];
    unsigned tos_new = (tos - 1) & rasLSMask;
    rasLSPointer[tid] = tos_new;
    return rasLinkStack[tos].block_addr;
}

template<class Impl>
void
ReturnAddrStack<Impl>::restore(unsigned top_entry_idx,
                         const Addr &restored_target)
{
    tos = top_entry_idx;

    addrStack[tos] = restored_target;
}

